﻿namespace EveKillboard.Core
{
    using System;
    using System.Collections.Generic;
    using System.ComponentModel.DataAnnotations;
    using System.Linq;
    using Base;
    using Enums;

    [Table("Killmails")]
    public class Killmail : KillmailBase
    {
        private IEnumerable<KillmailItem> _highslotItems = null;
        private IEnumerable<KillmailItem> _medslotItems = null;
        private IEnumerable<KillmailItem> _lowslotItems = null;
        private IEnumerable<KillmailItem> _rigslotItems = null;
        private IEnumerable<KillmailItem> _subslotItems = null;
        private IEnumerable<KillmailItem> _highslotCharges = null;
        private IEnumerable<KillmailItem> _medslotCharges = null;
        private IEnumerable<KillmailItem> _droneslotItems = null;
        private IEnumerable<KillmailItem> _cargoslotItems = null;


        [Key]
        [Column("ID")]
        public override int Id { get; set; }
        [Column("Date")]
        public override DateTime Date { get; set; }
        [Column("DamageReceived")]
        public override int Damage { get; set; }
        [Column("VictimID")]
        public override int CharacterId { get; set; }
        [Column("DestroyedID")]
        public override int? DestroyedId { get; set; }
        [Column("LocationID")]
        public override int LocationId { get; set; }
        [Column("Hash")]
        public override long Hash { get; set; }
        [Column("CorporationID")]
        public override int? CorporationId { get; set; }
        [Column("AllianceID")]
        public override int? AllianceId { get; set; }
        [Column("FactionID")]
        public override int? FactionId { get; set; }
        [Column("EveID")]
        public override int? EveId { get; set; }
        [Column("Created")]
        public override DateTime? Created { get; set; }
        [Column("Updated")]
        public override DateTime? Updated { get; set; }
        [Column("Year")]
        public Int16 Year { get; set; }
        [Column("Month")]
        public Int16 Month { get; set; }
        [Column("Week")]
        public Int16 Week { get; set; }
        
        [NotMapped]
        public IEnumerable<KillmailItem> ItemsFittedHighSlot
        {
            get
            {
                if(_highslotItems!=null) return _highslotItems;
                _highslotItems = new List<KillmailItem>();
                var items = Items.Where(i => !i.IsShip && !i.Cargo && !i.DroneBay && !i.Item.IsCharge && i.Item.SlotType.HasValue && i.Item.SlotType.Value == (short)SlotTypesEnum.High && (i.IsDestroyed || i.IsDropped)).ToArray();
                if (items.Any())
                {
                    AddRange(items, (List<KillmailItem>) _highslotItems, true);
                    AddRange(items, (List<KillmailItem>) _highslotItems, false);
                }
                return _highslotItems;
            }
        }
        [NotMapped]
        public IEnumerable<KillmailItem> ItemsFittedMedSlot
        {
            get
            {
                if(_medslotItems !=null) return _medslotItems;
                _medslotItems = new List<KillmailItem>();

                var items =  Items.Where(i =>
                        !i.IsShip && !i.Cargo && !i.DroneBay && !i.Item.IsCharge && i.Item.SlotType.HasValue && i.Item.SlotType.Value == (short)SlotTypesEnum.Medium && (i.IsDestroyed || i.IsDropped))
                        .ToArray();
                if (items.Any())
                {
                    AddRange(items, (List<KillmailItem>) _medslotItems, true);
                    AddRange(items, (List<KillmailItem>) _medslotItems, false);
                }
                return _medslotItems;
            }
        }
        [NotMapped]
        public IEnumerable<KillmailItem> ItemsFittedLowSlot
        {
            get
            {
                if(_lowslotItems !=null) return _lowslotItems;

                _lowslotItems = new List<KillmailItem>();

                var items = Items.Where(i => !i.IsShip && !i.Cargo && !i.DroneBay && !i.Item.IsCharge && i.Item.SlotType.HasValue && i.Item.SlotType.Value == (short)SlotTypesEnum.Low && (i.IsDestroyed || i.IsDropped)).ToArray();
                if (items.Any())
                {
                    AddRange(items, (List<KillmailItem>) _lowslotItems, true);
                    AddRange(items, (List<KillmailItem>) _lowslotItems, false);
                }
                return _lowslotItems;

            }
        }
        [NotMapped]
        public IEnumerable<KillmailItem> ItemsFittedRigSlot
        {
            get
            {
                if(_rigslotItems !=null) return _rigslotItems;

                _rigslotItems = new List<KillmailItem>();

                var items = Items.Where(i =>!i.IsShip && !i.Cargo && !i.DroneBay && !i.Item.IsCharge && i.Item.SlotType.HasValue && i.Item.SlotType.Value == (short)SlotTypesEnum.Rig && (i.IsDestroyed || i.IsDropped)).ToArray();
                if (items.Any())
                {
                    AddRange(items, (List<KillmailItem>) _rigslotItems, true);
                    AddRange(items, (List<KillmailItem>) _rigslotItems, false);
                }
                return _rigslotItems;

            }
        }
        [NotMapped]
        public IEnumerable<KillmailItem> ItemsFittedSubSlot
        {
            get
            {
                if(_subslotItems!=null) return _subslotItems;
                _subslotItems = Items.Where(i =>!i.IsShip && !i.Cargo && !i.DroneBay && !i.Item.IsCharge && i.Item.SlotType.HasValue && i.Item.SlotType.Value == (short)SlotTypesEnum.Subsystem && (i.IsDestroyed || i.IsDropped)).ToArray();
                return _subslotItems;
            }
        }
        [NotMapped]
        public IEnumerable<KillmailItem> ItemsDroneBay
        {
            get
            {
                if (_droneslotItems != null) return _droneslotItems;

                _droneslotItems = new List<KillmailItem>();

                var items = Items.Where(i => !i.IsShip && i.DroneBay && (i.IsDestroyed || i.IsDropped)).ToArray();
                if (items.Any())
                {
                    AddRange(items, (List<KillmailItem>) _droneslotItems, true);
                    AddRange(items, (List<KillmailItem>) _droneslotItems, false);
                }
                return _droneslotItems;
            }
        }
        [NotMapped]
        public IEnumerable<KillmailItem> ItemsCargo
        {
            get
            {
                if(_cargoslotItems !=null) return _cargoslotItems;
                _cargoslotItems = new List<KillmailItem>();

                var items = Items.Where(i => !i.IsShip && i.Cargo && !i.DroneBay && (i.IsDestroyed || i.IsDropped)).ToArray();
                if (items.Any())
                {
                    AddRange(items, (List<KillmailItem>) _cargoslotItems, true);
                    AddRange(items, (List<KillmailItem>) _cargoslotItems, false);
                }
                return _cargoslotItems;


            }
        }
        [NotMapped]
        public IEnumerable<KillmailItem> ChargesHighSlot
        {
            get
            {
                if(_highslotCharges !=null) return _highslotCharges;
                _highslotCharges = new List<KillmailItem>();

                var items = Items.Where(i =>!i.IsShip && !i.Cargo && !i.DroneBay && i.Item.IsCharge && i.Item.SlotType.HasValue && i.Item.SlotType.Value == (short)SlotTypesEnum.High && (i.IsDestroyed || i.IsDropped)).ToArray();
                if (items.Any())
                {
                    AddRange(items, (List<KillmailItem>) _highslotCharges, true);
                    AddRange(items, (List<KillmailItem>) _highslotCharges, false);
                }
                return _highslotCharges;

            }
        }
        [NotMapped]
        public IEnumerable<KillmailItem> ChargesMedSlot
        {
            get
            {
                if(_medslotCharges !=null) return _medslotCharges;
                _medslotCharges = new List<KillmailItem>();

                var items = Items.Where(i => !i.IsShip && !i.Cargo && !i.DroneBay && i.Item.IsCharge && i.Item.SlotType.HasValue && i.Item.SlotType.Value == (short)SlotTypesEnum.Medium && (i.IsDestroyed || i.IsDropped)).ToArray();
                if (items.Any())
                {
                    AddRange(items, (List<KillmailItem>) _medslotCharges, true);
                    AddRange(items, (List<KillmailItem>) _medslotCharges, false);
                }
                return _medslotCharges;
            }
        }
        [NotMapped]
        public IEnumerable<KillmailItem> Implants
        {
            get
            {
                return Items.Where(i => !i.IsShip && !i.Cargo && !i.DroneBay && i.Implant && (i.IsDestroyed || i.IsDropped));
            }
        }

        public decimal GetTotalValue()
        {
            return  (from killmailItem in Items
                    let itemValue = ItemValues.FirstOrDefault(i => i.ItemId == killmailItem.ItemId) 
                    where itemValue != null 
                    select (killmailItem.Quantity*itemValue.ItemValue))
                    .Sum();
        }

        public decimal GetShipValue()
        {
            return Destroyed.GroupId == 29 // Capsule
                       ? 0
                       : (from killmailItem in Items.Where(i => i.IsShip)
                          let itemValue = ItemValues.FirstOrDefault(i => i.ItemId == killmailItem.ItemId)
                          where itemValue != null
                          select (killmailItem.Quantity*itemValue.ItemValue))
                             .Sum();
        }

        public decimal GetModulesValue()
        {
            return Destroyed.GroupId == 29 // Capsule
                       ? 0
                       : (from killmailItem in Items.Where(i => !i.IsShip)
                          let itemValue = ItemValues.FirstOrDefault(i => i.ItemId == killmailItem.ItemId)
                          where itemValue != null
                          select (killmailItem.Quantity*itemValue.ItemValue))
                             .Sum();
        }

        public decimal GetDroppedValue()
        {
            return (from killmailItem in Items.Where(i => !i.IsShip && i.IsDropped)
                          let itemValue = ItemValues.FirstOrDefault(i => i.ItemId == killmailItem.ItemId)
                          where itemValue != null
                          select (killmailItem.Quantity * itemValue.ItemValue))
                        .Sum();
        }

        private static void AddRange(IEnumerable<KillmailItem> source, List<KillmailItem> dest, bool destroyed)
        {
            dest.AddRange(source.Where(i => destroyed ? i.IsDestroyed : i.IsDropped)
                              .GroupBy(item => item.Item)
                              .Select(g => new KillmailItem()
                              {
                                  IsDestroyed = destroyed,
                                  IsDropped = !destroyed,
                                  Item = g.Key,
                                  ItemId = g.Key.Id,
                                  Quantity = g.Sum(i => i.Quantity)
                              }));
        }

        public virtual Location Location { get; set; }
        public virtual Character Character { get; set; }
        public virtual Corporation Corporation { get; set; }
        public virtual Alliance Alliance { get; set; }
        public virtual Faction Faction { get; set; }
        public virtual Item Destroyed { get; set; }

        public virtual IList<Involved> Involved { get; set; }
        public virtual IList<KillmailItem> Items { get; set; }
        public virtual IList<KillmailItemValue> ItemValues { get; set; }
        
    }
}